﻿#include "shader.h"
#include "shader.h"
#include"../errorCheck/errorCheck.h"
#include<iostream>
#include<string>
#include<fstream>
#include<sstream>
using namespace std;
Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
	ifstream vShaderFile, fShaderFile;
	string vShaderCode, fShaderCode;
	vShaderFile.exceptions(ifstream::failbit | ifstream::badbit);
	fShaderFile.exceptions(ifstream::failbit | ifstream::badbit);
	try {
		//打开文件
		vShaderFile.open(vertexPath);
		fShaderFile.open(fragmentPath);
		stringstream vShaderStream, fShaderStream;
		vShaderStream<<vShaderFile.rdbuf();
		fShaderStream << fShaderFile.rdbuf();
		vShaderCode = vShaderStream.str();
		fShaderCode = fShaderStream.str();
		//关闭文件
		vShaderFile.close();
		fShaderFile.close();
	}
	catch (ifstream::failure& e) {
		cout << "Error: Shader File Error:" << e.what() << endl;
	}
	//2、创建Shader程序
	GLuint vertex, fragment;
	vertex = glCreateShader(GL_VERTEX_SHADER);
	fragment = glCreateShader(GL_FRAGMENT_SHADER);
	//3、为shader程序输入Shader源代码
	const char* vertexShaderSource = vShaderCode.c_str();
	const char* fragmentShaderSource = fShaderCode.c_str();

	glShaderSource(vertex, 1, &vertexShaderSource, NULL);//这个源码是一个字符串，所以第二个参数是1，最后一个参数可以为NULL，因为源码字符串以\0结尾了，会自动计算其长度
	glShaderSource(fragment, 1, &fragmentShaderSource, NULL);//这个源码是一个字符串，所以第二个参数是1，最后一个参数可以为NULL，因为源码字符串以\0结尾了，会自动计算其长度
	//4、为shader代码执行编译并输出错误信息

	glCompileShader(vertex);
	checkError(vertex, Compile);
	glCompileShader(fragment);
	checkError(fragment,Compile);

	//5、创建一个Shader的壳子
	mProgram = glCreateProgram();
	//6、将编译好的两个Shader放到这个壳子里面
	glAttachShader(mProgram, vertex);
	glAttachShader(mProgram, fragment);
	//7、执行链接并检查链接错误
	glLinkProgram(mProgram);
	checkError(mProgram, Link);
	//清理Shader
	glDeleteShader(vertex);
	glDeleteShader(fragment);

}
Shader::~Shader()
{
}

void Shader::begin()
{
	GL_CALL(glUseProgram(mProgram));
}

void Shader::end()
{
	GL_CALL(glUseProgram(0));

}

void Shader::setFloat(const std::string& name, float value)
{
	//通过名称拿到Uniform变量的位置
	GLuint location = glGetUniformLocation(mProgram, name.c_str());
	//通过Location更新uniform的值
	glUniform1f(location, value);
}
void Shader::setVector3(const std::string& name, float value1, float value2, float value3)
{
	GLuint location = glGetUniformLocation(mProgram, name.c_str());
	GL_CALL(glUniform3f(location, value1, value2, value3));
}
void Shader::setVector3(const std::string& name, const float* value)
{
	GLuint location=glGetUniformLocation(mProgram, name.c_str());
	GL_CALL(glUniform3fv(location, 1, value));
}
void Shader::setInt(const std::string& name, int value)
{
	GLuint location = glGetUniformLocation(mProgram, name.c_str());
	glUniform1i(location, value);
}
void Shader::setMetric4x4(const std::string& name, glm::mat4 mat)
{
	GLuint location = glGetUniformLocation(mProgram, name.c_str());
	//第二个参数为传入的矩阵个数，第四个参数表示用于构造矩阵的数组，而第三个表示是否对第四个传入进行转置，
	// 因为OpenGL和GLM中默认都是列优先的，比如对于数组{1,2,3,4}，OpenGL和GLM中构造出来的矩阵是第一列是1,2，第二列是3,4，
	//而其他很多数学库是行优先的，即第一行！！（是行不是列）是1,2，第二行是3,4,第三个参数就是求是否要用这种的变换，其实就是相当于用默认列优先方式传入后在做转置
	glUniformMatrix4fv(location, 1,false,glm::value_ptr(mat));
}

void Shader::checkError(GLuint target, Operation operation)
{
	int success = 0;
	char infoLog[1024];
	if (operation == Compile) {
		glGetShaderiv(target, GL_COMPILE_STATUS, &success);
		memset(infoLog, 0, sizeof(char) * 1024);
		if (!success) {
			glGetShaderInfoLog(target, 1024, NULL, infoLog);
			std::cout << "Error: Shader Compile Error:" << infoLog << std::endl;
		}
	}
	else if(operation==Link){
		glGetProgramiv(mProgram, GL_LINK_STATUS, &success);
		if (!success) {
			glGetProgramInfoLog(mProgram, 1024, NULL, infoLog);
			std::cout << "Error: Shader Link Error:" << infoLog << std::endl;
		}
	}
}
